﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Mindfor.Web.Data;
using System.Collections.ObjectModel;
using NHibernate;
using NHibernate.Linq;

namespace Mindfor.Web
{
	/// <summary>
	/// Represents option collection for single module.
	/// </summary>
	public class ModuleOptionCollection : ICollection<ModuleOption>
	{
		/// <summary>
		/// Gets or sets items.
		/// </summary>
		protected IList<ModuleOption> Items { get; set; }

		/// <summary>
		/// Gets module name collection attached to.
		/// </summary>
		public string ModuleName { get; private set; }

		/// <summary>
		/// Initialies new instance.
		/// </summary>
		/// <param name="moduleName">Module name that collection is attached to.</param>
		/// <param name="options">Initial options.</param>
		public ModuleOptionCollection(string moduleName, IEnumerable<ModuleOption> options)
		{
			ModuleName = moduleName;
			foreach (ModuleOption o in options)
				CheckModuleName(o);
			Items = new List<ModuleOption>(options);
		}

		/// <summary>
		/// Gets option by index.
		/// </summary>
		/// <param name="index">Index.</param>
		public ModuleOption this[int index]
		{
			get { return Items[index]; }
		}

		/// <summary>
		/// Gets option by name.
		/// </summary>
		/// <param name="name">Option name.</param>
		/// <returns>Module option or null if not found.</returns>
		public ModuleOption this[string name]
		{
			get { return Items.FirstOrDefault(r => r.OptionName == name); }
		}

		/// <summary>
		/// Gets options count.
		/// </summary>
		public int Count
		{
			get { return Items.Count; }
		}

		/// <summary>
		/// Gets if collection is readonly.
		/// </summary>
		public bool IsReadOnly
		{
			get { return false; }
		}

		/// <summary>
		/// Adds new option to collection.
		/// </summary>
		/// <param name="item">Module option.</param>
		public void Add(ModuleOption item)
		{
			CheckModuleName(item);
			Items.Add(item);
		}

		/// <summary>
		/// Clears all options.
		/// </summary>
		public void Clear()
		{
			Items.Clear();
		}

		/// <summary>
		/// Determines whether collection contains item.
		/// </summary>
		/// <param name="item">Item to search.</param>
		public bool Contains(ModuleOption item)
		{
			return Items.Contains(item);
		}

		/// <summary>
		///  Copies the elements of the Collection to an Array, starting at a particular index.
		/// </summary>
		/// <param name="array">
		/// The one-dimensional System.Array that is the destination of the elements
		/// copied from System.Collections.Generic.ICollection<T>. The System.Array must
		/// have zero-based indexing.
		/// </param>
		/// <param name="arrayIndex">The zero-based index in array at which copying begins.</param>
		public void CopyTo(ModuleOption[] array, int arrayIndex)
		{
			Items.CopyTo(array, arrayIndex);
		}		

		/// <summary>
		/// Removes option from collection.
		/// Not implemented.
		/// </summary>
		/// <param name="item">Item to remove.</param>
		/// <returns>true if item was successfully removed; otherwise, false.
		/// This method also returns false if item is not found.</returns>
		public bool Remove(ModuleOption item)
		{
			throw new NotImplementedException();
			//return Items.Remove(item);
		}

		public IEnumerator<ModuleOption> GetEnumerator()
		{
			return Items.GetEnumerator();
		}

		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
		{
			return Items.GetEnumerator();
		}

		/// <summary>
		/// Saves changes made to options.
		/// </summary>
		/// <param name="data">Data session used to save options.</param>
		public void Save(ISession data)
		{
			using (ITransaction t = data.BeginTransaction())
			{
				foreach (ModuleOption o in Items)
				{
					ModuleOption option = data.MergeEntity(o);
					data.SaveOrUpdate(option);
				}
				t.Commit();
			}
		}

		/// <summary>
		/// Reload options from data source.
		/// </summary>
		/// <param name="data">Data session used to load options.</param>
		public void Reload(ISession data)
		{
			Items = data.Query<ModuleOption>()
				.Where(r => r.ModuleName == ModuleName)
				.ToList();
		}

		/// <summary>
		/// Validates that item.ModuleName matches ModuleName.
		/// Throws exception if not valid.
		/// </summary>
		/// <param name="item">Item to validate.</param>
		protected void CheckModuleName(ModuleOption item)
		{
			if (item.ModuleName != null && item.ModuleName != ModuleName)
				throw new InvalidOperationException("Cannot add module option with invalid ModuleName.");
			item.ModuleName = ModuleName;
		}
	}
}
